#include <iostream>
#include "crossbit/MemoryManager.h"

using namespace crossbit;

MemoryManager* MemoryManager::instance = NULL;

MemoryManager::MemoryManager() : pool((XTMemAddr)NULL), size(0)
{
				ele_curr_ubound = -1;
				for(XTUint32 i = 0; i < ELEMENT_NUM; i++)
				{
									ele_pool[i] = (XTMemAddr)NULL;
									ele_size[i] = 0;
				}		
	
				void *buf_exe = SYSCALL::XTMmap((void*)POOL_START_EXE, POOLSIZE_EXE, PROT_EXEC | PROT_READ | PROT_WRITE, MAP_FIXED | MAP_ANON | MAP_PRIVATE, 0, 0 );

				if( buf_exe != MAP_FAILED )
				{
								pool_exe = (XTMemAddr)buf_exe;
								size_exe = POOLSIZE_EXE;
				}
				else
				{
								UTIL::XTLOG("MMAP error in MemoryManager!\n");
								SYSCALL::XTExit(-1);
				}

				UTIL::XTMemset( (void*)pool_exe, 0, size_exe );

				void *buf = SYSCALL::XTMmap((void*)POOL_START, POOLSIZE, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_ANON | MAP_PRIVATE, 0, 0 );
				if( buf != MAP_FAILED )
				{
								pool = (XTMemAddr)buf;
								size = POOLSIZE;
				}
				else
				{
								UTIL::XTLOG("MMAP error in MemoryManager!\n");
								SYSCALL::XTExit(-1);
				}
}

MemoryManager::~MemoryManager()
{
				//empty now
}

MemoryManager* MemoryManager::getInstance()
{
				if( instance == NULL )
				{
								instance = new MemoryManager();
				}

				return instance;
}

XTMemAddr MemoryManager::allocateExecutable( XTUint32 size_req )
{
				XTMemAddr exe_pool_addr;

				//align by 16 bytes
				size_req = (size_req + 15) & 0xFFFFFFF0;

				if( size_req <= size_exe )
				{
								exe_pool_addr = (XTMemAddr)pool_exe;
								UTIL::XTMemset( (void*)pool_exe, 0, size_req );
								pool_exe += size_req;
								size_exe -= size_req;
				}
				else
				{
								//it's temporarily now
								//allocate new pool here
								UTIL::XTLOG("Memory for exe is not enough!\n");
								exit(0);
				}

				return exe_pool_addr;
}

XTMemAddr MemoryManager::allocateObjectPool(XTUint32 * pool_id, XTUint32 pool_size, XTUint32 obj_size )
{

				XTMemAddr obj_addr;
				//align by 16 bytes
				pool_size = (pool_size + 15) & 0xFFFFFFF0;

				if( pool_size <= size )
				{
								ele_curr_ubound++;
								if(ele_curr_ubound > 15){ //pool id is 15 at most
									UTIL::XTLOG("Pool id is not enough.\n");
									exit(0);
								}
								*pool_id = ele_curr_ubound;
								ele_pool[*pool_id] = (XTMemAddr)pool;
								ele_size[*pool_id] = pool_size;
								UTIL::XTMemset( (void*)pool, 0, pool_size );
								pool += pool_size;
								size -= pool_size;
				}
				else
				{
								//it's temporarily now
								//allocate new pool here
								UTIL::XTLOG("Memory is not enough.\n");
								exit(0);
				}
				obj_addr = allocateObject(*pool_id, obj_size);

				return obj_addr;
}

XTMemAddr MemoryManager::allocateObject(XTUint32 pool_id, XTUint32 size_req)
{	
				if(pool_id > ele_curr_ubound)
				{
								UTIL::XTLOG("Pool id:%u doesn't exist.\n", pool_id);
								exit(0);
				}
				
				XTMemAddr buf;

				//align by 16 bytes
				size_req = (size_req + 15) & 0xFFFFFFF0;

				if( size_req <= ele_size[pool_id] )
				{
								buf = ele_pool[pool_id];
								ele_pool[pool_id] += size_req;
								ele_size[pool_id] -= size_req;
				}
				else
				{
								//it's temporarily now
								//allocate new pool here
								UTIL::XTLOG("Memory of pool %u is not enough.\n", pool_id);
								exit(0);
				}

				return buf;
}
